import jQuery from 'jquery';
import { trim, toUpper, isFunction } from 'lodash';
import { getAttrs } from './xmlParser';
import PropsWatcher from './PropsWatcher';
import { COMPONENT_ID, COMPONENT_NAME } from './const';

const createdID = () => {
  let id = 0;
  return () => {
    return id++;
  }
};
let JqComponent_ID = createdID();

function create$$($dom) {
  return function find(selector) {
    let childen = $dom.find(selector);
    $dom.parent().find(selector).each((index, $d) => {
      if ($dom.get(0) === $d) {
        childen.push($d)
      }
    });
    return childen;
  };
}

function setAttr($fragment, attrs) {
  Object.keys(attrs).forEach(key => {
    let values = attrs[key];
    values.forEach(v => {
      let lastedVal = $fragment.attr(key);
      let attrVals = lastedVal ? `${lastedVal} ${v}` : v;
      $fragment.attr(key, attrVals);
    });
  });
}

var mountedStylesTagNameMap = {};

/**
 * 这里用tagName判断，但是tagName会有hash碰撞的可能
 * @param {*} styles 
 * @param {*} tagName 
 */
function mountedStyles(styles, tagName) {
  if (!mountedStylesTagNameMap[tagName] && styles) {
    jQuery("head").append(`<style>${styles}</style>`);
    mountedStylesTagNameMap[tagName] = true;
  }
}

function proxy$$($$) {
  var handler = {
    get(target, key) {
      if (!target[key]) {
        return jQuery[key];
      }
      return target[key];
    }
  };
  return new Proxy($$, handler);
}



const setComponentInfo2Dom = (dom, tagName, id) => {
  if (id) {
    jQuery(dom).attr(COMPONENT_ID, id);
  }
  if (tagName) {
    jQuery(dom).attr(COMPONENT_NAME, tagName);
  }
}

// const eventHooks = function (id, dom, attrs) {
//   Object.keys(attrs).forEach(attr => {
//     if (startsWith(attr, 'on')) {
//       dom[attr] = () => {
//         let lisfn = attrs[attr][0];
//         var fnName = lisfn.replace(/\(.+?\)/g, '')
//         var arg = lisfn.match(/\((.+?)\)/g);
//         arg = arg[0].replace(/\(|\)/g, '').split(',').map(item => trim(item));
//         listenProps.emit(id, fnName, arg, dom);
//       }
//     }
//   })
// }

export let allComponents = {};

let globalComponents = {};

export const listenProps = new PropsWatcher();

export const setGlobalComponent = function (name, jqc) {
  globalComponents[name] = jqc;
  return globalComponents;
};

export const removeGlobalComponent = function (name) {
  delete globalComponents[name];
  return globalComponents;
}
export default class JqComponent {
  constructor(options = {}) {
    this.options = options;
    let { template, styles, components, ready } = this.options;
    this.template = template;
    this.$fragment = jQuery(template);
    this.components = globalComponents;
    this.styles = styles;
    this.ready = ready;
    this.id = JqComponent_ID();
    this.slots = {};

    Object.keys(components || {}).forEach(key => {
      this.components[toUpper(key)] = components[key];
    });

    allComponents[this.id] = this;
  }

  render(root) {
    const tagName = root.tagName;
    if (tagName) {
      // 处理插槽
      if (tagName === 'SLOT') {
        const slotName = root.name ? root.name : 'default';
        const slotElem = this.slots[slotName];
        if (slotElem) {
          root.parentNode.replaceChild(slotElem, root)
        }
      }
      const options = this.components[tagName];
      const attrs = getAttrs(root);
   
      if (options) {
        const jqc = new JqComponent(options);

        // 保存插槽
        const slots = trim(root.innerHTML);
        if (slots) {
          jQuery(slots).each((idx, node) => {
            if (node.nodeType == 1) {
              const slotName = node.slot ? node.slot : 'default';
              jqc.slots[slotName] = node;
            }
          })
        }

        setAttr(jqc.$fragment, attrs);

        root.parentNode.replaceChild(jqc.$fragment.get(0), root)
        setComponentInfo2Dom(jqc.$fragment, null, jqc.id);
        mountedStyles(jqc.styles, tagName);
        jqc.render(jqc.$fragment.get(0));

        if (isFunction(jqc.ready)) {
          const $$ = create$$(jqc.$fragment);
          const watcher = (key, val) => listenProps.on(jqc.id, key, val);
          $$.jqc = jqc;
          $$.on = watcher;
          $$.el = jqc.$fragment;
          jqc.ready.apply(jqc.$fragment, [proxy$$($$)]);
        }
      }
    }
    root.childNodes.forEach(child => {
      this.render(child);
    });
  }
};